{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "import keras \n",
    "from keras.datasets import mnist \n",
    "from keras.models import Sequential \n",
    "from keras.layers import Dense,Reshape\n",
    "from keras.optimizers import Adam \n",
    "import random\n",
    "from keras.layers import Input,Dense,Concatenate\n",
    "from keras.models import Model\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  为了更好地演示tensorflow serving对应模型为多输入的情况，设计如下任务：\n",
    "- 输入为\n",
    "  - minist数据集中的一张图片,图片中数字为p\n",
    "  - 一个整数n，0<=n<=9\n",
    "- 输出为\n",
    "    - p*n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "#### 构造数据集\n",
    "#minist图片数据集\n",
    "(x_train_photo, y_train_photo), (x_test_photo, y_test_photo) = mnist.load_data() \n",
    "def make_data_instance(x_photo, y_photo):\n",
    "    n = random.randint(0,10)\n",
    "    label = y_photo\n",
    "    return x_photo,n,label\n",
    "train_data = [ make_data_instance(x,y)  for x,y in zip(x_train_photo, y_train_photo)]\n",
    "test_data = [ make_data_instance(x,y)  for x,y in zip(x_test_photo, y_test_photo)]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "#处理数据\n",
    "def process_data(train_data):\n",
    "    x_photo,n,label = zip(*train_data)\n",
    "    x_photo = np.array(x_photo) \n",
    "    n  = np.array(n) \n",
    "    label = np.array(label) \n",
    "    return  x_photo,n,label\n",
    "train_photo,train_n,train_label = process_data(train_data)\n",
    "test_photo,test_n,test_label = process_data(test_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [],
   "source": [
    "photo_input_raw = Input(shape=(28,28), dtype='float32', name='photo_input')\n",
    "photo_input = Reshape(target_shape=(28*28,))(photo_input_raw)\n",
    "number_input = Input(shape=(1,),dtype='float32',name='number_input')\n",
    "all_input = Concatenate()([number_input,photo_input])\n",
    "dense1 = Dense(256, activation='relu', use_bias=True)(all_input)\n",
    "dense2 = Dense(128,activation='relu',use_bias=True)(dense1)\n",
    "output = Dense(1)(dense2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [],
   "source": [
    "model=Model( inputs=[photo_input_raw,number_input],outputs=output )\n",
    "model.compile(loss=keras.losses.MAE, optimizer=keras.optimizers.Adam(), metrics=['mae'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "60000/60000 [==============================] - 2s 28us/step - loss: 3.7984 - mean_absolute_error: 3.7984\n",
      "Epoch 2/10\n",
      "60000/60000 [==============================] - 1s 19us/step - loss: 1.0236 - mean_absolute_error: 1.0236\n",
      "Epoch 3/10\n",
      "60000/60000 [==============================] - 1s 18us/step - loss: 0.8495 - mean_absolute_error: 0.8495\n",
      "Epoch 4/10\n",
      "60000/60000 [==============================] - 1s 19us/step - loss: 0.7448 - mean_absolute_error: 0.7448\n",
      "Epoch 5/10\n",
      "60000/60000 [==============================] - 1s 18us/step - loss: 0.6827 - mean_absolute_error: 0.6827\n",
      "Epoch 6/10\n",
      "60000/60000 [==============================] - 1s 19us/step - loss: 0.6267 - mean_absolute_error: 0.6267\n",
      "Epoch 7/10\n",
      "60000/60000 [==============================] - 1s 19us/step - loss: 0.5838 - mean_absolute_error: 0.5838\n",
      "Epoch 8/10\n",
      "60000/60000 [==============================] - 1s 19us/step - loss: 0.5447 - mean_absolute_error: 0.5447\n",
      "Epoch 9/10\n",
      "60000/60000 [==============================] - 1s 18us/step - loss: 0.5034 - mean_absolute_error: 0.5034\n",
      "Epoch 10/10\n",
      "60000/60000 [==============================] - 1s 19us/step - loss: 0.4740 - mean_absolute_error: 0.4740\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<keras.callbacks.History at 0x7f42a141ccf8>"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.fit([train_photo,train_n],train_label,batch_size=128,epochs=10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10000/10000 [==============================] - 0s 37us/step\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[0.499525762796402, 0.499525762796402]"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.evaluate([test_photo,test_n],test_label)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[6.9661   ]\n",
      " [2.056283 ]\n",
      " [1.0896876]]\n",
      "[7 2 1]\n"
     ]
    }
   ],
   "source": [
    "#查看测试结果\n",
    "print(model.predict([test_photo[:3],test_n[:3]]))\n",
    "print(test_label[:3])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.save('keras_photo_mutiply_n.h5')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
